package com.ztesoft.zsmart.zcm.dialing.util;

import com.ztesoft.dcc.message.Avp;
import com.ztesoft.dcc.message.Dcc;
import com.ztesoft.zsmart.core.log.ZSmartLogger;
import com.ztesoft.zsmart.zcm.dialing.domain.vo.AvpVO;
import com.ztesoft.zsmart.zcm.dialing.domain.vo.Config;
import com.ztesoft.zsmart.zcm.dialing.domain.vo.DccVO;
import com.ztesoft.zsmart.zcm.dialing.domain.vo.Diameter;
import com.ztesoft.zsmart.zcm.dialing.domain.vo.Request;
import com.ztesoft.zsmart.zcm.dialing.domain.vo.Response;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public abstract class XML2DccUtils {
    private static ZSmartLogger logger = ZSmartLogger.getLogger(XML2DccUtils.class);

    public static Config anaysisXML(String xmlStr) throws DocumentException {
        Document document = DocumentHelper.parseText(xmlStr);
        return anaysisXML(document);
    }

    private static Config anaysisXML(Document document) throws DocumentException {
        Element root = document.getRootElement();
        Config config = new Config();
        Iterator iter = root.elementIterator();
        while (iter.hasNext()) {
            Element element = (Element) iter.next();
            DccVO dcc = new DccVO();
            setAttr(dcc, element.attributes());
            Iterator iterInner = element.elementIterator();
            while (iterInner.hasNext()) {
                Element elementInner = (Element) iterInner.next();
                Map<String, Object> map = parseMsg(elementInner);
                String name = elementInner.getName().toUpperCase();
                if ("REQUEST".equals(name)) {
                    Request request = new Request();
                    request.setDiameter((Diameter) map.get("diameter"));
                    request.setAvps((List<AvpVO>) map.get("avps"));
                    dcc.setRequest(request);
                }
                else if ("RESPONSE".equals(name)) {
                    Response response = new Response();
                    response.setDiameter((Diameter) map.get("diameter"));
                    response.setAvps((List<AvpVO>) map.get("avps"));
                    dcc.setResponse(response);
                }
            }
            config.getMsgs().add(dcc);
        }
        return config;
    }

    private static void setAttr(DccVO dcc, List attrList) {
        Iterator dccAttr = attrList.iterator();
        while (dccAttr.hasNext()) {
            Attribute attr = (Attribute) dccAttr.next();
            String name = attr.getName();
            switch (name) {
                case "id":
                    dcc.setId(attr.getText());
                    break;
                case "key":
                    dcc.setKey(attr.getText());
                    break;
                case "name":
                    dcc.setName(attr.getText());
                    break;
                case "disconn_send":
                    dcc.setDisconnSend(attr.getText());
                    break;
                case "conn_send":
                    dcc.setConnSend(attr.getText());
                    break;
                case "disable_log":
                    dcc.setDisableLog(attr.getText());
                    break;
                case "timer_send":
                    dcc.setTimerSend(attr.getText());
                    break;
                default:
                    break;
            }
        }
    }
    private static Map<String, Object> parseMsg(Element elementInner) {
        Map<String, Object> result = new HashMap<>();
        for (Iterator dccInner = elementInner.elementIterator(); dccInner.hasNext();) {
            Element elementDccInner = (Element) dccInner.next();
            String name = elementDccInner.getName();
            if ("diameter".equalsIgnoreCase(name)) {
                result.put("diameter", parseDiameter(elementDccInner));
            }
            else if ("avps".equalsIgnoreCase(name)) {
                result.put("avps", parseAvps(elementDccInner));
            }
        }
        return result;
    }

    private static Diameter parseDiameter(Element element) {
        Diameter diameter = new Diameter();
        for (Iterator dccAttr = element.elementIterator(); dccAttr.hasNext();) {
            Element diameterElement = (Element) dccAttr.next();
            String name = diameterElement.getName();
            String text = diameterElement.getText();
            if ("application_id".equalsIgnoreCase(name)) {
                diameter.setApplicationId(text);
            }
            else if ("command_code".equalsIgnoreCase(name)) {
                diameter.setCommandCode(text);
            }
            else if ("command_flags".equalsIgnoreCase(name)) {
                diameter.setCommandFlags(text);
            }
            else if ("end_to_end_identifier".equalsIgnoreCase(name)) {
                diameter.setEndToEndIdentifier(text);
            }
            else if ("hop_by_hop_identifier".equalsIgnoreCase(name)) {
                diameter.setHopByHopIdentifier(text);
            }
            else if ("version".equalsIgnoreCase(name)) {
                diameter.setVersion(text);
            }
        }
        return diameter;
    }

    private static List<AvpVO> parseAvps(Element element) {
        List<AvpVO> avps = new ArrayList<>();
        Iterator avpIter = element.elementIterator();
        while (avpIter.hasNext()) {
            avps.add(parseAvp((Element) avpIter.next()));
        }
        return avps;
    }

    private static AvpVO parseAvp(Element element) {
        AvpVO avp = new AvpVO();
        Iterator dccAttr = element.attributes().iterator();
        while (dccAttr.hasNext()) {
            Attribute attr = (Attribute) dccAttr.next();
            String name = attr.getName();
            String text = attr.getText();
            if ("alias".equalsIgnoreCase(name)) {
                avp.setAlias(text);
            }
            else if ("code".equalsIgnoreCase(name)) {
                avp.setCode(text);
            }
            else if ("customize".equalsIgnoreCase(name)) {
                avp.setCustomize(text);
            }
            else if ("expression".equalsIgnoreCase(name)) {
                avp.setExpression(text);
            }
            else if ("flags".equalsIgnoreCase(name)) {
                avp.setFlags(text);
            }
            else if ("name".equalsIgnoreCase(name)) {
                avp.setName(text);
            }
            else if ("option".equalsIgnoreCase(name)) {
                avp.setOption(text);
            }
            else if ("type".equalsIgnoreCase(name)) {
                avp.setType(text);
            }
            else if ("vendor_id".equalsIgnoreCase(name)) {
                avp.setVendorId(text);
            }
            else if ("desc".equalsIgnoreCase(name)) {
                avp.setDesc(text);
            }
        }
        avp.setAvpValue(element.getText());
        avp.setAvp(parseAvps(element));
        return avp;
    }

    public static Config convertDcc(Dcc dcc) {
        Config config = new Config();
        List<DccVO> msgs = new ArrayList<>();
        config.setMsgs(msgs);
        DccVO dccVo = new DccVO();
        int commandFlags = 0;
        Diameter diameter = new Diameter();
        diameter.setApplicationId(dcc.getApplicationId() + "");
        diameter.setCommandCode(dcc.getCommandCode() + "");
        diameter.setEndToEndIdentifier(dcc.getEndbyendId() + "");
        diameter.setHopByHopIdentifier(dcc.getHopbyhopId() + "");
        diameter.setVersion(dcc.getVersion() + "");
        try {
            diameter.setMessageLength(dcc.toBytes().length);
        }
        catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        List<AvpVO> avpVoList = new ArrayList<>();
        convertAvp(dcc.getAvpList(), avpVoList);
        if (dcc.pFlag()) {
            commandFlags += 64;
        }
        if (dcc.eFlag()) {
            commandFlags += 32;
        }
        if (dcc.tFlag()) {
            commandFlags += 16;
        }
        if (dcc.rFlag()) {
            commandFlags += 128;
            diameter.setCommandFlags(commandFlags + "");
            Request request = new Request();
            request.setDiameter(diameter);
            request.setAvps(avpVoList);
            dccVo.setRequest(request);
        }
        else {
            diameter.setCommandFlags(commandFlags + "");
            Response response = new Response();
            response.setDiameter(diameter);
            response.setAvps(avpVoList);
            dccVo.setResponse(response);
        }
        config.getMsgs().add(dccVo);
        return config;
    }

    public static Dcc convertDccVo(DccVO dccVo) {
        Dcc dcc = new Dcc();
        Diameter diameter = null;
        List<AvpVO> avpVoList = null;
        if (dccVo.getRequest() != null) {
            diameter = dccVo.getRequest().getDiameter();
            avpVoList = dccVo.getRequest().getAvps();
            dcc.setrFlag(true);
        }
        else {
            diameter = dccVo.getResponse().getDiameter();
            avpVoList = dccVo.getResponse().getAvps();
            dcc.setrFlag(false);
        }
        String applicationIdStr = diameter.getApplicationId();
        if (StringUtils.isNotEmpty(applicationIdStr)) {
            dcc.setApplicationId(Integer.parseInt(applicationIdStr));
        }
        dcc.setCommandCode(Integer.parseInt(diameter.getCommandCode()));
        if (StringUtils.isEmpty(diameter.getHopByHopIdentifier())) {
            diameter.setHopByHopIdentifier("0");
        }
        if (StringUtils.isEmpty(diameter.getEndToEndIdentifier())) {
            diameter.setEndToEndIdentifier("0");
        }
        dcc.setHopbyhopId(new BigInteger(diameter.getHopByHopIdentifier()));
        dcc.setEndbyendId(new BigInteger(diameter.getEndToEndIdentifier()));
        int commandFlags = 0;
        if (StringUtils.isNotEmpty(diameter.getCommandFlags())) {
            commandFlags = Integer.parseInt(diameter.getCommandFlags());
        }
        if ((commandFlags & 128) == 128) {
            dcc.setrFlag(true);
        }
        else {
            dcc.setrFlag(false);
        }
        if ((commandFlags & 64) == 64) {
            dcc.setpFlag(true);
        }
        else {
            dcc.setpFlag(false);
        }
        if ((commandFlags & 32) == 32) {
            dcc.seteFlag(true);
        }
        else {
            dcc.seteFlag(false);
        }
        if ((commandFlags & 16) == 16) {
            dcc.settFlag(true);
        }
        else {
            dcc.settFlag(false);
        }
        List<Avp> avpList = new ArrayList<>();
        if (avpVoList != null) {
            convertAvpVo(avpList, avpVoList);
        }
        dcc.getAvpList().addAll(avpList);
        return dcc;
    }

    private static void convertAvp(List<Avp> avpList, List<AvpVO> avpVoList) {
        Iterator iterator = avpList.iterator();
        while (iterator.hasNext()) {
            Avp avp = (Avp) iterator.next();
            AvpVO avpVo = new AvpVO();
            avpVo.setName(avp.getName());
            avpVo.setType(avp.getType());
            avpVo.setCode(avp.getCode() + "");
            avpVo.setAvpValue(avp.getValue());
            avpVo.setType(avpVo.getType());
            avpVo.setVendorId(avp.getVendorId() + "");
            int flags = 0;
            if (avp.ismFlag()) {
                flags += 64;
            }
            if (avp.ispFlag()) {
                flags += 32;
            }
            avpVo.setFlags(flags + "");
            avpVoList.add(avpVo);
            if (avp.getSubAvpList() != null) {
                avpVo.setAvp(new ArrayList<>());
                convertAvp(avp.getSubAvpList(), avpVo.getAvp());
            }
        }
    }

    private static void convertAvpVo(List<Avp> avpList, List<AvpVO> avpVoList) {
        Iterator iterator = avpVoList.iterator();
        while (iterator.hasNext()) {
            AvpVO avpVo = (AvpVO) iterator.next();
            Avp avp = new Avp();
            avp.setName(avpVo.getName());
            avp.setType(avpVo.getType());
            try {
                avp.setCode(Integer.parseInt(avpVo.getCode()));
            }
            catch (Exception e) {
                //                e.printStackTrace();
            }
            avp.setValue(avpVo.getAvpValue().trim());
            int flags = 0;
            if (avpVo.getFlags() != null && !"".equals(avpVo.getFlags())) {
                flags = Integer.parseInt(avpVo.getFlags());
            }
            if ((flags & 64) == 64) {
                avp.setpFlag(true);
            }
            else {
                avp.setpFlag(false);
            }
            if ((flags & 32) == 32) {
                avp.setmFlag(true);
            }
            else {
                avp.setmFlag(false);
            }
            if (avpVo.getVendorId() != null && !"".equals(avpVo.getVendorId())) {
                try {
                    avp.setVendorId(Long.parseLong(avpVo.getVendorId()));
                }
                catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
            avpList.add(avp);
            if (avpVo.getAvp() != null) {
                avp.setSubAvpList(new ArrayList<>());
                convertAvpVo(avp.getSubAvpList(), avpVo.getAvp());
            }
        }
    }

    public static DccVO getRequestById(String hopByHopId, String endToEndId, Config config) {
        for (DccVO dccVo : config.getMsgs()) {
            Diameter diamter = dccVo.getRequest().getDiameter();
            if (diamter.getHopByHopIdentifier().equals(hopByHopId) && diamter.getEndToEndIdentifier()
                .equals(endToEndId)) {
                return dccVo;
            }
        }
        return null;
    }

    public static String getCCRequestNumber(List<Avp> avpList) {
        for (Avp avp : avpList) {
            if (avp.getName().equals("CC-Request-Number")) {
                return avp.getValue();
            }
        }
        for (Avp avp : avpList) {
            return getCCRequestNumber(avp.getSubAvpList());
        }
        return "";
    }

    public static String getCCRequestNumber2(List<AvpVO> avpList) {
        for (AvpVO avp : avpList) {
            if (avp.getName().equals("CC-Request-Number")) {
                return avp.getAvpValue();
            }
        }
        for (AvpVO avp : avpList) {
            return getCCRequestNumber2(avp.getAvp());
        }
        return "";
    }

    public static String getServiceContextId(List<Avp> avpList) {
        for (Avp avp : avpList) {
            if (avp.getName().equals("Service-Context-Id")) {
                return avp.getValue().split("@")[0];
            }
        }
        for (Avp avp : avpList) {
            return getServiceContextId(avp.getSubAvpList());
        }
        return "";
    }

    public static String getServiceContextId2(List<AvpVO> avpList) {

        for (AvpVO avp : avpList) {
            if (avp.getName().equals("Service-Context-Id")) {
                return avp.getAvpValue().split("@")[0];
            }
        }
        for (AvpVO avp : avpList) {
            return getServiceContextId2(avp.getAvp());
        }
        return "Capabilities-Exchange";

    }

    public static AvpVO getAvp(String avpField, String avpValue, List<AvpVO> avpList)
        throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        if (avpList != null) {
            for (AvpVO avp : avpList) {
                String value = BeanUtils.getProperty(avp, avpField);
                if (StringUtils.isNotEmpty(value) && value.equals(avpValue)) {
                    return avp;
                }
            }
            for (AvpVO avp : avpList) {
                return getAvp(avpField, avpValue, avp.getAvp());
            }
        }
        return null;
    }

}
